using System;
using System.Collections.Generic;
using FarseerGames.FarseerPhysics;
using FarseerGames.FarseerPhysics.Collisions;
using BriansClimb.Climber;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;
using Microsoft.Xna.Framework.Content;
using BriansClimb.DrawableGameObjects.GameScenes;
using BriansClimb.DrawableGameObjects.ClimbingHolds;
using BriansClimb.DrawableGameObjects.Buttons;
using BriansClimb.GameObjects;

namespace BriansClimb.GameObjects
{
    /// <summary>
    /// This is a game component that manages the communication between the save/load function
    /// of the FileManager and the addition and storing of climbing hold on the wall.
    /// </summary>
    public class WallManager : Microsoft.Xna.Framework.GameComponent
    {
        protected Vector2 climbingHoldPosition;
        protected List<Vector2> climbingHoldList;
        protected List<Texture2D> climbingHoldTextureList;
        protected List<ClimbingHold> climbingObjectHoldList;
        protected WallCustomizeScene wallCustomizeScene;
        protected PhysicsSimulator physicsSimulator;
        protected BodyPart tempHold;
        protected Game briansClimb;
        protected bool clicked = false;
        protected bool pressed = false;

        protected AudioManager audioManager;
        protected List<GameComponent> wallManagerComponents;

        protected int tileX;
        protected int tileY;
        protected int tileWidth = 30;
        protected int tileHeight = 30;
        protected int climbingHoldCount;
        protected int climbingHoldArrayIndex;
        protected int frictionAttribute;
        protected string climbingHoldType;
        protected Vector2 holdPosition;
        protected Vector2 mousePosition;
        protected MouseState mouseState;
        protected MouseState oldMouseState;
        
        protected KeyboardState oldKeyboardState;

        protected GameObjects.BodyPartInfoHandler bpih;
       
        /// <summary>
        /// Default Constructor
        /// </summary>
        /// <param name="game">Main Game Object</param>
        /// <param name="wallCustomizeScene">WallCustomizeScene object</param>
        /// <param name="physSimIn">Physics Simulator Object</param>
        /// <param name="bpihIn"></param>
        /// <param name="compListIn"></param>
        public WallManager(Game game, WallCustomizeScene wallCustomizeScene, PhysicsSimulator physSimIn, 
                 BodyPartInfoHandler bpihIn, List<GameComponent> compListIn)
            : base(game)
        {
            bpih = bpihIn;
            climbingHoldList = new List<Vector2>();
            audioManager = (AudioManager)game.Services.GetService(typeof(AudioManager));
            climbingHoldTextureList = new List<Texture2D>();
            climbingObjectHoldList = new List<ClimbingHold>();
            this.wallCustomizeScene = wallCustomizeScene;
            physicsSimulator = physSimIn;
            wallManagerComponents = compListIn;
            briansClimb = game;
        }

        /// <summary>
        /// Sets the x/y coordinates of the currently placed climbing hold.
        /// </summary>
        /// <param name="holdPosition">x/y coordinates of the most recently place climbing hold.</param>
        public void SetClimbingHoldPosition(Vector2 holdPosition)
        {
            climbingHoldPosition = holdPosition;
        }

        /// <summary>
        /// Returns the List of Climbing Hold Objects.
        /// </summary>
        public List<ClimbingHold> ClimbingHoldList
        {
            get { return climbingObjectHoldList; }
            set { climbingObjectHoldList = value; }
        }

        /// <summary>
        /// Returns/Sets the wall manager's component list.
        /// </summary>
        public List<GameComponent> WallManagerComponents
        {
            get { return wallManagerComponents; }
            set { wallManagerComponents = value; }
        }        

        /// <summary>
        /// Returns/Sets the String Variable Deciding the Type of Climbing Hold.
        /// </summary>
        public string ClimbingHoldType
        {
            get { return climbingHoldType; }
            set { climbingHoldType = value; }
        }

        /// <summary>
        /// Returns the Friction Value Associated with the Climbing hold
        /// </summary>
        public int HoldFriction
        {
            get { return frictionAttribute; }
        }
        
        /// <summary>
        /// Checks for a right mouse click (press & release)
        /// </summary>
        /// <returns></returns>
        private bool CheckRightClick()
        {                        
            mouseState = Mouse.GetState();
            bool mouseResult = (oldMouseState.RightButton == ButtonState.Pressed &&
                                mouseState.RightButton == ButtonState.Released);
            oldMouseState = mouseState;
            return mouseResult;
        }

        /// <summary>
        /// Checks for the left mouse button click(press & release).
        /// </summary>
        private void CheckButtonClick()
        {
            mouseState = Mouse.GetState();
            clicked = false;

            if (mouseState.LeftButton == ButtonState.Pressed)
                pressed = true;

            if (mouseState.LeftButton == ButtonState.Released && pressed)
            {
                clicked = true;
                pressed = false;
            }
        }

        /// <summary>
        /// Allows the game component to update itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public override void Update(GameTime gameTime)
        {
            tempHold = new BodyPart(briansClimb);

            if (CheckRightClick())
            {
                climbingHoldArrayIndex = 0;

                mouseState = Mouse.GetState();

                //Calculate X and Y coordinates
                tileX = (int)Math.Floor((float)(mouseState.X / tileWidth));
                tileY = (int)Math.Floor((float)(mouseState.Y / tileHeight));

                //Set the climbing hold to be drawn to a whole tile coordinate
                holdPosition.X = (tileX * tileWidth) + (tileWidth / 2);
                holdPosition.Y = (tileY * tileHeight) + (tileHeight / 2);
                
                //retreive int values for hold positions
                int mouseX = (int)holdPosition.X;
                int mouseY = (int)holdPosition.Y;
                
                //change the mouse position to hover right over the hold origin
                Mouse.SetPosition(mouseX, mouseY);

                mousePosition = new Vector2(mouseState.X, mouseState.Y);

                List<ClimbingHold> tempList = climbingObjectHoldList;
     
                //going through the climbing hold array
                while (climbingHoldArrayIndex < climbingObjectHoldList.Count)
                {
                    //found the matching hold
                    if (mousePosition == climbingObjectHoldList[climbingHoldArrayIndex].ClimbingHoldPosition)
                    {
                        //set the hold to invisible
                        climbingObjectHoldList[climbingHoldArrayIndex].Visible = false;
                        audioManager.PlayCue("deletehold");
                        //remove invisible holds
                        removeInactiveHolds();
                        climbingObjectHoldList.Remove(climbingObjectHoldList[climbingHoldArrayIndex]);                        
                    }
                    climbingHoldArrayIndex++;
                }
                
            }

            bool holdAlreadyThere=false;

            foreach (ClimbingHold ch in climbingObjectHoldList)
            {
                if (climbingHoldPosition == ch.ClimbingHoldPosition)
                    holdAlreadyThere = true;
            }

            CheckButtonClick();

            /* checks if the left mouse is clicked, the click is somewhere on the climbing wall, and
             * if there isn't already a hold there.
             */
            if (clicked && (Mouse.GetState().Y < 954) && (!holdAlreadyThere))
            {
                //checks if the current List of climbing holds has less than 50 items
                if (climbingObjectHoldList.Count < 50)
                {
                    //adds current ClimbingHold object to the end of the List

                    //the final(normalized) friction value of the hold is calculated
                    switch(climbingHoldType)
                    {
                        case "rest":
                            frictionAttribute = 0;
                            break;
                        case "jug":
                            frictionAttribute = wallCustomizeScene.FrictionSlider.getFinalAttribute + 1;
                            break;
                        case "slope":
                            frictionAttribute = wallCustomizeScene.FrictionSlider.getFinalAttribute + 6;
                            break;
                        case "crimp":
                            frictionAttribute = wallCustomizeScene.FrictionSlider.getFinalAttribute + 11;
                            break;
                        case "foot":
                            frictionAttribute = wallCustomizeScene.FrictionSlider.getFinalAttribute + 16;
                            break;
                    }                                        
                    
                    //the new climbing hold is added to the list
                    climbingObjectHoldList.Add(new ClimbingHold(briansClimb,climbingHoldPosition,
                                climbingHoldType, frictionAttribute, tempHold));
                    audioManager.PlayCue("placehold");
                }
                //if the current List of climbing holds has 50 items
                else
                    wallCustomizeScene.ShowWarning();
            }

            bpih.setWallList(climbingObjectHoldList);
            removeInactiveHolds();
            base.Update(gameTime);
        }

        /// <summary>
        /// Removes holds that are not visible from the components list so they will not be drawn.
        /// </summary>
        public void removeInactiveHolds()
        {
            foreach (ClimbingHold holdObject in climbingObjectHoldList)
            {
                //check to see if the hold object already exists in the components list
                if (!wallManagerComponents.Contains(holdObject))
                    wallManagerComponents.Add(holdObject);                
                if (!holdObject.Visible)
                {
                    //removes the hold from the component list if it is not visible
                    wallManagerComponents.Remove(holdObject);
                }
            }
        }
 
    }
}
